﻿#if DOTNET35 // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

    using System.Diagnostics;
    using System.Runtime.InteropServices;

    namespace System.Numerics
    {
        [StructLayout(LayoutKind.Explicit)]
        internal struct DoubleUlong
        {
            [FieldOffset(0)]
            public double dbl;
            [FieldOffset(0)]
            public ulong uu;
        }

        internal static class NumericsHelpers
        {
            private const int kcbitUint = 32;

            public static void GetDoubleParts(double dbl, out int sign, out int exp, out ulong man, out bool fFinite)
            {

                DoubleUlong du;
                du.uu = 0;
                du.dbl = dbl;

                sign = 1 - ((int)(du.uu >> 62) & 2);
                man = du.uu & 0x000FFFFFFFFFFFFF;
                exp = (int)(du.uu >> 52) & 0x7FF;
                if (exp == 0)
                {
                    // Denormalized number.
                    fFinite = true;
                    if (man != 0)
                        exp = -1074;
                }
                else if (exp == 0x7FF)
                {
                    // NaN or Infinite.
                    fFinite = false;
                    exp = int.MaxValue;
                }
                else
                {
                    fFinite = true;
                    man |= 0x0010000000000000;
                    exp -= 1075;
                }
            }

            public static double GetDoubleFromParts(int sign, int exp, ulong man)
            {
                DoubleUlong du;
                du.dbl = 0;

                if (man == 0)
                    du.uu = 0;
                else
                {
                    // Normalize so that 0x0010 0000 0000 0000 is the highest bit set.
                    int cbitShift = CbitHighZero(man) - 11;
                    if (cbitShift < 0)
                        man >>= -cbitShift;
                    else
                        man <<= cbitShift;
                    exp -= cbitShift;
                    Debug.Assert((man & 0xFFF0000000000000) == 0x0010000000000000);

                    // Move the point to just behind the leading 1: 0x001.0 0000 0000 0000
                    // (52 bits) and skew the exponent (by 0x3FF == 1023).
                    exp += 1075;

                    if (exp >= 0x7FF)
                    {
                        // Infinity.
                        du.uu = 0x7FF0000000000000;
                    }
                    else if (exp <= 0)
                    {
                        // Denormalized.
                        exp--;
                        if (exp < -52)
                        {
                            // Underflow to zero.
                            du.uu = 0;
                        }
                        else
                        {
                            du.uu = man >> -exp;
                            Debug.Assert(du.uu != 0);
                        }
                    }
                    else
                    {
                        // Mask off the implicit high bit.
                        du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong)exp << 52);
                    }
                }

                if (sign < 0)
                    du.uu |= 0x8000000000000000;

                return du.dbl;
            }

            // Do an in-place two's complement. "Dangerous" because it causes
            // a mutation and needs to be used with care for immutable types.
            public static void DangerousMakeTwosComplement(uint[] d)
            {
                if (d != null && d.Length > 0)
                {
                    d[0] = unchecked(~d[0] + 1);

                    int i = 1;
                    // first do complement and +1 as long as carry is needed
                    for (; d[i - 1] == 0 && i < d.Length; i++)
                    {
                        d[i] = unchecked(~d[i] + 1);
                    }
                    // now ones complement is sufficient
                    for (; i < d.Length; i++)
                    {
                        d[i] = ~d[i];
                    }
                }
            }

            public static ulong MakeUlong(uint uHi, uint uLo)
            {
                return ((ulong)uHi << kcbitUint) | uLo;
            }

            public static uint Abs(int a)
            {
                unchecked
                {
                    uint mask = (uint)(a >> 31);
                    return ((uint)a ^ mask) - mask;
                }
            }

            public static uint CombineHash(uint u1, uint u2)
            {
                return ((u1 << 7) | (u1 >> 25)) ^ u2;
            }

            public static int CombineHash(int n1, int n2)
            {
                return unchecked((int)CombineHash((uint)n1, (uint)n2));
            }

            public static int CbitHighZero(uint u)
            {
                if (u == 0)
                    return 32;

                int cbit = 0;
                if ((u & 0xFFFF0000) == 0)
                {
                    cbit += 16;
                    u <<= 16;
                }
                if ((u & 0xFF000000) == 0)
                {
                    cbit += 8;
                    u <<= 8;
                }
                if ((u & 0xF0000000) == 0)
                {
                    cbit += 4;
                    u <<= 4;
                }
                if ((u & 0xC0000000) == 0)
                {
                    cbit += 2;
                    u <<= 2;
                }
                if ((u & 0x80000000) == 0)
                    cbit += 1;
                return cbit;
            }

            public static int CbitHighZero(ulong uu)
            {
                if ((uu & 0xFFFFFFFF00000000) == 0)
                    return 32 + CbitHighZero((uint)uu);
                return CbitHighZero((uint)(uu >> 32));
            }
        }
    }
#endif